
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>

#include "./lib/string_rld.h"
#include "./lib/merge_sort.h"
#include "./lib/selection_sort.h"

void help ();

void version();

void mergesort(char* inputs []);

void selsort(char* inputs []);

string readData (FILE* pFile);

string readDataFromFile (string pFileName);

string readDataFromFiles (string inputs []);

int main (int argc, char **argv)
{
  int siguiente_opcion;
  void (*cmd) () = NULL;
  void (*cmd_sort) (char* inputs []) = mergesort; // Por default se utiliza el algoritmo de ordenamiento merge.
    
  /* Una cadena que lista las opciones cortas validas */
  const char* const op_cortas = "hvms" ;

  /* Una estructura de varios arrays describiendo los valores largos */
  const struct option op_largas[] =
  {
      { "help",    0,  NULL,   'h'},
      { "version", 0,  NULL,   'v'},
      { "merge",   0,  NULL,   'm'},
      { "sel",     0,  NULL,   's'},
      { NULL,      0,  NULL,   0  }
  };
  
  while (1)
  {
      /* Llamamos a la funcion getopt */
      siguiente_opcion = getopt_long (argc, argv, op_cortas, op_largas, NULL);
    
      if (-1 == siguiente_opcion)
          break; /* No hay mas opciones. Rompemos el bucle */

      switch (siguiente_opcion)
      {
          case 'h' : /* -h o --help */
              cmd = help;
              break;
              
          case 'v' : /* -v o --version */
              cmd = version;
              break;
        
          case 'm' : /* -o --merge */
              cmd_sort = mergesort;
              break;

          case 's' : /* -s --sel */
              cmd_sort = selsort;
              break;
                      
          case '?' : /* opcion no valida */
               cmd = help;
              break;

          default : /* No especifica argumentos.  Se selecciona el metodo merge y se lee de stdin */
              cmd_sort = mergesort;
      }
  }
  
  /* Leo la lista de nombres de archivo de entrada.*/
  char* inputs [(argc - optind) + 1];
  inputs[0] = NULL;  
  
  if (optind < argc)
  {
      int cur = 0;
      while (optind < argc)
          inputs[cur++] = argv[optind++];
          inputs[cur] = NULL;          
  }  

  if (NULL != cmd)
    cmd(); /* Se selecciono la opcion help o version.  ejecuto el comando asociado. */
  else
     cmd_sort(inputs); /* Se selecciono la opcion merge o sel.  Ejecuto el comando asociado. */   
  
  /* Salida del programa principal */
  exit;
  return 0;

    
} // main

void help ()
{
    printf("\ntp0 [OPTIONS] [File ...]\n");
    printf("    -h,  --help           display this help and exit.\n"
           "    -v,  --version        display version information and exit.\n"
           "    -m,  --merge          use the mergesort algorithm.\n"
           "    -s,  --sel            use the selectionsort algorithm.\n\n"
          );
} // help

void version()
{
    printf("\n\ntp0 version 15\n\n");

} // version

void mergesort(string inputs []){

    if (NULL == inputs) return;
    
    string data;
    
    if (NULL == inputs[0])
        data = readData (stdin);
    else
        data = readDataFromFiles (inputs);
    
    char result [strlen(data) + 1];
    result [0] = '\0';
    merge_sort (result, data);
    
    fprintf(stdout, "%s", result);
    
    free (data);
    
} // mergesort

void selsort(string inputs []) {
    
    if (NULL == inputs) return;
    
    string data;
    
    if (NULL == inputs[0])
        data = readData (stdin);
    else
        data = readDataFromFiles (inputs);
    
    selection_sort(data);
    
    fprintf (stdout, "%s", data);
    
    free (data);
    
}

string readDataFromFiles (string inputs [])
{
    string data = malloc(sizeof(char));
    if (NULL == data)
    {
        fprintf (stderr, "%s", "Memoria insuficiente.");
        exit(1);
    }
    data[0]='\0';
    
    int cur = 0;
    while (NULL != inputs [cur])
    {
        string dataAux = readDataFromFile (inputs[cur++]);
        
        string aux = malloc(strlen(data) +  strlen(dataAux) + 1);
        if (NULL == aux)
        {
            fprintf (stderr, "%s", "Memoria insuficiente.");
            exit (1);
        }
        aux[0]='\0';

        aux = strcat(aux, data);
        free(data);
        
        aux = strcat(aux,dataAux);
        free(dataAux);
        
        data = aux;
        
    }
    
    return data;
    
} // selsort

string readDataFromFile (string pFileName) 
{
    
    FILE* file;
    file = fopen (pFileName, "rb");
    if (NULL == file)
    {
        fprintf(stderr, "%s %s", "No se puede abrir para lectura el fichero ", pFileName);
        exit(2);
    }
    
    string data = readData(file);
    fclose(file);
    
    return data;
    
}

string readData (FILE* pFile) 
{
    
    string data = malloc(sizeof(char));
    
    if (NULL == data)
    {
        fprintf(stderr, "%s", "Memoria insuficiente.");
        exit(1);
    }
    
    data [0]='\0';
    
    int cnt = 0;
    char buffer [100];
    
    while (cnt = fread(buffer, sizeof(char), 99, pFile))
    {
        buffer [cnt] = '\0';

        string aux = malloc(strlen(data) + strlen(buffer) + 1);
        if (NULL == aux)
        {
            fprintf(stderr, "%s", "Memoria insuficiente.");
            exit(1);
        }
        aux [0] = '\0';
        
        aux = strcat (aux, data);
        free (data);

        aux = strcat (aux, buffer);
        data = aux;
    }
    
    return data;

} // readData

string sel_sort (string result, string data)
{
    result = strcpy (result, data);
}
